package com.hqd.ch03.v35.web.servlet.mvc.condition;


import com.hqd.ch03.utils.CollectionUtils;
import com.hqd.ch03.v35.utils.StringUtils;
import com.hqd.ch03.v35.web.utils.UrlPathHelper;
import org.apache.commons.lang3.ObjectUtils;

import javax.servlet.http.HttpServletRequest;
import java.util.*;

/**
 * url匹配
 */
public class PatternsRequestCondition extends AbstractRequestCondition<PatternsRequestCondition> {
    private final static Set<String> EMPTY_PATH_PATTERN = Collections.singleton("");

    private Set<String> patterns = new LinkedHashSet<>();

    public PatternsRequestCondition(Set<String> patterns) {
        this.patterns = patterns;
    }

    public PatternsRequestCondition(String... patterns) {
        this.patterns.addAll(Arrays.asList(patterns));
    }

    private static Set<String> initPatterns(String[] patterns) {
        if (!hasPattern(patterns)) {
            return EMPTY_PATH_PATTERN;
        }
        Set<String> result = new LinkedHashSet<>(patterns.length);
        for (String pattern : patterns) {
            if (StringUtils.hasLength(pattern) && !pattern.startsWith("/")) {
                pattern = "/" + pattern;
            }
            result.add(pattern);
        }
        return result;
    }

    private static boolean hasPattern(String[] patterns) {
        if (!ObjectUtils.isEmpty(patterns)) {
            for (String pattern : patterns) {
                if (StringUtils.hasText(pattern)) {
                    return true;
                }
            }
        }
        return false;
    }

    public Set<String> getPatterns() {
        return this.patterns;
    }

    @Override
    protected Collection<String> getContent() {
        return this.patterns;
    }

    public boolean isEmptyPathMapping() {
        return this.patterns == EMPTY_PATH_PATTERN;
    }

    public Set<String> getDirectPaths() {
        if (isEmptyPathMapping()) {
            return EMPTY_PATH_PATTERN;
        }
        Set<String> result = Collections.emptySet();
        for (String pattern : this.patterns) {
            result = (result.isEmpty() ? new HashSet<>(1) : result);
            result.add(pattern);
        }
        return result;
    }

    @Override
    public PatternsRequestCondition combine(PatternsRequestCondition other) {
        if (isEmptyPathMapping() && other.isEmptyPathMapping()) {
            return this;
        } else if (other.isEmptyPathMapping()) {
            return this;
        } else if (isEmptyPathMapping()) {
            return other;
        }
        Set<String> result = new LinkedHashSet<>();
        if (!this.patterns.isEmpty() && !other.patterns.isEmpty()) {
            for (String pattern1 : this.patterns) {
                for (String pattern2 : other.patterns) {
                    result.add(pattern1.concat(pattern2));
                }
            }
        }
        return new PatternsRequestCondition(result);
    }

    @Override
    public PatternsRequestCondition getMatchingCondition(HttpServletRequest request) {
        String lookupPath = UrlPathHelper.getResolvedLookupPath(request);
        List<String> matches = getMatchingPatterns(lookupPath);
        return !CollectionUtils.isEmpty(matches) ? new PatternsRequestCondition(new LinkedHashSet<>(matches)) : null;
    }

    public List<String> getMatchingPatterns(String lookupPath) {
        List<String> matches = null;
        for (String pattern : this.patterns) {
            String match = getMatchingPattern(pattern, lookupPath);
            if (match != null) {
                matches = (matches != null ? matches : new ArrayList<>());
                matches.add(match);
            }
        }
        return matches;
    }

    private String getMatchingPattern(String pattern, String lookupPath) {
        if (pattern.equals(lookupPath)) {
            return pattern;
        }
        return null;
    }

    @Override
    public int compareTo(PatternsRequestCondition other, HttpServletRequest request) {
        Iterator<String> iterator = this.patterns.iterator();
        Iterator<String> iteratorOther = other.patterns.iterator();
        while (iterator.hasNext() && iteratorOther.hasNext()) {
            boolean result = iterator.next().equals(iteratorOther.next());
            if (result) {
                return 1;
            }
        }
        if (iterator.hasNext()) {
            return -1;
        } else if (iteratorOther.hasNext()) {
            return 1;
        } else {
            return 0;
        }
    }
}
